package cn.org.donald.antivirus;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * @author Donald
 * @since 2023-09-21 19:09:36
 **/
public class ScanVirusTest {


    public static void main(String[] args) throws IOException, InterruptedException {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setBufferRequestBody(false);
        requestFactory.setConnectTimeout(1000 * 60 * 60);
        requestFactory.setReadTimeout(1000 * 60 * 60);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        final String SECRET = "79D391B7F9BF4EC7B5226F1DE1745FE818AD47E81F2";
        // 1.获取token
        String accessToken = getAccessToken(restTemplate, SECRET);
        System.out.println("成功获取accessToken ：" + accessToken);
        // 2.创建扫描任务
        String taskId = createScanTask(restTemplate, accessToken);
        System.out.println("成功创建扫描任务 ：" + taskId);
        // 3.上传文件
//        final String taskId = "3018df5b13324f709d696daf5f4c8f1aST202311020001020";
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        for (int i = 0; i < 1; i++) {
            Path path = Paths.get("D:\\five-thousand\\" + i + ".txt");
            executorService.submit(() -> uploadFile(path, restTemplate, accessToken, taskId));
        }
        executorService.shutdown();
        if (!executorService.awaitTermination(10, TimeUnit.HOURS)) {
            throw new RemoteException();
        }
//        uploadFile(Paths.get("D:\\迅雷下载\\virus.zip"), restTemplate, accessToken, taskId);
//        uploadFile(Paths.get("D:\\迅雷下载\\测试文件夹.rar"), restTemplate, accessToken, taskId);
//        uploadFile(Paths.get("D:\\迅雷下载\\用完删除.7z"), restTemplate, accessToken, taskId);
//        uploadFile(Paths.get("D:\\迅雷下载\\用完删除.rar"), restTemplate, accessToken, taskId);
//        uploadFile(Paths.get("D:\\迅雷下载\\3g测试.zip"), restTemplate, accessToken, taskId);
        System.out.println("成功上传文件");
        // 4.提交扫毒任务
        commitTask(restTemplate, accessToken, taskId);

    }

    public static void splitChunk(Path path, Consumer<ChunkItem> consumer) {
        int chunkSize = (int) Math.pow(1024, 2) * 30;
        byte[] bytes = new byte[chunkSize];
        File file = path.toFile();
        long length = file.length();
        int chunkNum = (int) Math.ceil((double) length / chunkSize);
        long alreadyRead = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(7);
        long start = System.currentTimeMillis();
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(path.toFile(), "r")) {
            for (int i = 0; i < chunkNum; i++) {
                ChunkItem chunkItem = new ChunkItem();
                randomAccessFile.seek((long) i * chunkSize);
                int read = randomAccessFile.read(bytes);
                byte[] chunkArray = Arrays.copyOfRange(bytes, 0, read);
                chunkItem.setBytes(chunkArray);
                chunkItem.setFilename(file.getName());
                chunkItem.setTotalLength(length);
                chunkItem.setChunkSize(chunkSize);
                chunkItem.setChunkIndex(i);
                chunkItem.setChunkNum(chunkNum);
                chunkItem.setCurrentRead(read);
                alreadyRead += read;
                chunkItem.setAlreadyRead(alreadyRead);
                executorService.submit(() -> consumer.accept(chunkItem));
            }
            executorService.shutdown();
            while (true) {
                if (executorService.isTerminated()) {
                    System.out.println("总耗时： " + ((System.currentTimeMillis() - start) / 1000) + "秒");
                    return;
                }
                TimeUnit.SECONDS.sleep(1);
            }
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static String getAccessToken(RestTemplate restTemplate, String secret) {
        String forObject = restTemplate.getForObject("http://192.168.0.73:9041/boxapi/open-api/v1/" + secret + "/access-token", String.class);
        JSONObject jsonObject = JSON.parseObject(forObject);
        JSONObject data = jsonObject.getJSONObject("data");
        return data.getString("accessToken");
    }

    private static String createScanTask(RestTemplate restTemplate, String accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Gamma-Access-Token", accessToken);
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        ResponseEntity<String> exchange = restTemplate.exchange("http://192.168.0.73:9041/boxapi/open-api/v1/create-scan-task", HttpMethod.GET, entity, String.class);
        JSONObject jsonObject = JSON.parseObject(exchange.getBody());
        JSONObject data = jsonObject.getJSONObject("data");
        return data.getString("taskId");
    }

    private static void uploadFile(Path path, RestTemplate restTemplate, String accessToken, String taskId) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Gamma-Access-Token", accessToken);
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        /*Path path = Paths.get("D:\\迅雷下载\\3g测试.zip");*/
        splitChunk(path, chunkItem -> {
            ByteArrayResource byteArrayResource = new ByteArrayResource(chunkItem.getBytes()) {
                @Override
                public String getFilename() {
                    return chunkItem.getFilename();
                }
            };

            MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
            map.add("fileName", chunkItem.getFilename());
            map.add("chunkIndex", chunkItem.getChunkIndex());
            map.add("chunkData", byteArrayResource);
            map.add("chunkSize", chunkItem.getChunkSize());
            map.add("currentChunkSize", chunkItem.getCurrentRead());
            map.add("totalChunk", chunkItem.getChunkNum());
            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(map, headers);
            ResponseEntity<String> exchange = restTemplate.postForEntity("http://192.168.0.73:9041/boxapi/open-api/v1/" + taskId + "/upload-file", entity, String.class);
            System.out.println(exchange.getBody());
            BigDecimal percentage = BigDecimal.valueOf(chunkItem.getAlreadyRead())
                    .multiply(BigDecimal.valueOf(100))
                    .divide(BigDecimal.valueOf(chunkItem.getTotalLength()), 2, RoundingMode.HALF_UP);
            System.out.println("完成百分比：" + percentage + "%" + "总大小：" + chunkItem.getTotalLength() + "已读大小：" + chunkItem.getAlreadyRead());
        });
    }

    private static void commitTask(RestTemplate restTemplate, String accessToken, String taskId) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Gamma-Access-Token", accessToken);
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        long start = System.currentTimeMillis();
        try {
            ResponseEntity<String> exchange = restTemplate.exchange("http://192.168.0.73:9041/boxapi/open-api/v1/" + taskId + "/commit-task", HttpMethod.GET, entity, String.class);
            System.out.println(exchange.getBody());
        } finally {
            System.out.println("耗时：" + (System.currentTimeMillis() - start));
        }
    }

    public static class ChunkItem {
        private byte[] bytes;
        private String filename;
        private int chunkIndex;
        private int chunkSize;
        private long totalLength;
        private int chunkNum;
        private long alreadyRead;
        private int currentRead;

        public byte[] getBytes() {
            return bytes;
        }

        public void setBytes(byte[] bytes) {
            this.bytes = bytes;
        }

        public String getFilename() {
            return filename;
        }

        public void setFilename(String filename) {
            this.filename = filename;
        }

        public int getChunkSize() {
            return chunkSize;
        }

        public void setChunkSize(int chunkSize) {
            this.chunkSize = chunkSize;
        }

        public long getTotalLength() {
            return totalLength;
        }

        public void setTotalLength(long totalLength) {
            this.totalLength = totalLength;
        }

        public int getChunkIndex() {
            return chunkIndex;
        }

        public void setChunkIndex(int chunkIndex) {
            this.chunkIndex = chunkIndex;
        }

        public int getChunkNum() {
            return chunkNum;
        }

        public void setChunkNum(int chunkNum) {
            this.chunkNum = chunkNum;
        }

        public long getAlreadyRead() {
            return alreadyRead;
        }

        public void setAlreadyRead(long alreadyRead) {
            this.alreadyRead = alreadyRead;
        }

        public int getCurrentRead() {
            return currentRead;
        }

        public void setCurrentRead(int currentRead) {
            this.currentRead = currentRead;
        }
    }
}
